package sns.app.nhwc;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.util.ArrayList;
import org.apache.catalina.websocket.MessageInbound;
import org.apache.catalina.websocket.WsOutbound;

public class NHWCMessageInbound extends MessageInbound {

	// different types of the instructions
	public static final int MOUSE_DOWN = 0;
	public static final int MOUSE_MOVE = 1;
	public static final int MOUSE_UP = 2;
	public static final int PENCIL = 3;
	public static final int RUBBER = 4;
	public static final int CLEAR_ALL = 5;
	public static final int GAME_INIT = 6;
	public static final int GAME_DRAWER = 7;
	public static final int GAME_GUESSER = 8;
	public static final int GAME_PREPARE = 9;
	public static final int GAME_ROOM_NUM = 10;
	public static final int GAME_PLAYER_NUM = 11;
	public static final int USERLIST_ADD = 12;
	public static final int USERLIST_REMOVE = 13;
	// different states of the player
	public static final int NOT_PREPARING = 0;
	public static final int PREPARING = 1;
	public static final int DRAWER_NOT_DRAWING = 2;
	public static final int DRAWER_DRAWING = 3;
	public static final int GUESSER_GUESSING = 4;

	private static final String PROTOCOL_PREFIX = "#@#&";

	private int iduser;
	private String nickname;
	private int state;
	private NHWCGame game;

	public NHWCMessageInbound(int iduser, String nickname, NHWCGame game) {
		this.iduser = iduser;
		this.nickname = nickname;
		this.setState(NOT_PREPARING);
		this.game = game;
	}

	@Override
	protected void onOpen(WsOutbound outbound) {
		if (game.addPlayer(this)) {
			String message = String.format("* %s %s", nickname, "建立连接。");
			sendMessageTo(getProtocolFormat(GAME_INIT), this);
			broadcast(message, game.getConnections());
			broadcast(
					getProtocolFormat(GAME_PLAYER_NUM, game.getPlayerNum(),
							game.getPreparedNum()), game.getConnections());
			// get recent user list
			for(NHWCMessageInbound mib:game.getConnections()){
				if(mib.iduser!=this.iduser){
					sendMessageTo(getProtocolFormat(USERLIST_ADD, mib.iduser, mib.nickname), this);
				}
			}
			// send userinfo
			broadcast(getProtocolFormat(USERLIST_ADD, iduser, nickname), game.getConnections());
		} else {
			sendMessageTo("加入游戏失败，请重新选择游戏。", this);
		}
	}

	@Override
	protected void onClose(int status) {
		game.removePlayer(this);
		String message = String.format("* %s %s", nickname, "断开连接。");
		broadcast(message, game.getConnections());
		// remove userinfo
		broadcast(getProtocolFormat(USERLIST_REMOVE, iduser, nickname), game.getConnections());
	}

	@Override
	protected void onBinaryMessage(ByteBuffer message) throws IOException {
		throw new UnsupportedOperationException("Binary message not supported.");
	}

	@Override
	protected void onTextMessage(CharBuffer message) throws IOException {
		// if the message is an instruction
		if (message.length() >= 4
				&& message.toString().substring(0, 4).equals(PROTOCOL_PREFIX)) {
			handleInstruction(message.toString());
		} else {
			// Never trust the client
			handleMessage(HTMLFilter.filter(message.toString()));
		}
	}

	/**
	 * handle the normal message
	 * 
	 * @param message
	 */
	private void handleMessage(String message) {
		String tmpMessage = String.format("%s: %s", nickname, message);
		broadcast(tmpMessage, game.getConnections());
		game.checkWhetherToFinish(message, this);
	}

	/**
	 * handle the instruction message
	 * 
	 * @param message
	 *            whose format should be
	 *            "[PROTOCOL_PREFIX][ins_type] [ins_x] [ins_y]"
	 */
	private void handleInstruction(String message) {
		String msgtmp = message.substring(4);
		int indextmp1 = msgtmp.indexOf(" ");
		int instruction = Integer.parseInt(msgtmp.substring(0, indextmp1));
		switch (instruction) {
		case GAME_PREPARE:
			this.setState(PREPARING);
			game.increasePreparedNum();
			broadcast("* " + this.nickname + "已经准备。", game.getConnections());
			broadcast(
					getProtocolFormat(NHWCMessageInbound.GAME_PLAYER_NUM,
							game.getPlayerNum(), game.getPreparedNum()),
					game.getConnections());
			game.checkWhetherToStart();
			break;
		case GAME_INIT:
			game.initGame();
			broadcast("* 画家结束了游戏，没人猜对，真可惜！", game.getConnections());
			broadcast(message, game.getConnections());
			break;
		default:
			broadcast(message, game.getConnections());
			break;
		}
	}

	/**
	 * broadcast the message to all the clients in connections
	 * 
	 * @param message
	 * @param connections
	 */
	public static void broadcast(String message,
			ArrayList<NHWCMessageInbound> connections) {
		for (NHWCMessageInbound connection : connections) {
			sendMessageTo(message, connection);
		}
	}

	/**
	 * send the message to the connection
	 * 
	 * @param message
	 * @param connection
	 */
	public static void sendMessageTo(String message,
			NHWCMessageInbound connection) {
		try {
			CharBuffer buffer = CharBuffer.wrap(message);
			connection.getWsOutbound().writeTextMessage(buffer);
		} catch (IOException ignore) {
			// Ignore
		}
	}

	/**
	 * get the instruction message in valid format
	 * 
	 * @param instructionType
	 * @param x
	 * @param y
	 * @return
	 */
	public static String getProtocolFormat(int instructionType, int x, int y) {
		return String.format("%s%d %d %d", PROTOCOL_PREFIX, instructionType, x,
				y);
	}

	/**
	 * get the instruction message in valid format
	 * 
	 * @param instructionType
	 * @return
	 */
	public static String getProtocolFormat(int instructionType) {
		return getProtocolFormat(instructionType, -1, -1);
	}
	
	/**
	 * get the format of sending message related to listing users
	 * 
	 * @param instructionType
	 * @param iduser
	 * @param nickname
	 * @return
	 */
	public static String getProtocolFormat(int instructionType, int iduser, String nickname) {
		return String.format("%s%d %d %s", PROTOCOL_PREFIX, instructionType, iduser, nickname);
	}

	/**
	 * get state
	 * 
	 * @return
	 */
	public int getState() {
		return state;
	}

	/**
	 * set state
	 * 
	 * @param state
	 */
	public void setState(int state) {
		this.state = state;
	}
	
	public int getIduser(){
		return iduser;
	}
	
	public String getNickname(){
		return nickname;
	}
	
}
